package com.bensonbasement.snorflux.client;

import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.swing.SwingUtilities;

import com.bensonbasement.snorflux.messages.fromserver.WorldTimeMessage;

/**
 * This class accepts world update messages
 * and, at a controlled rate, feeds them to the swing
 * client.
 * @author garrison
 *
 */
public class WorldUpdateManager {
	public interface UpdateHandler {
		public void handleWorldTimeMessage(WorldTimeMessage message);
		public void outOfUpdates();
	}
	
	private final BlockingQueue<WorldTimeMessage> updateQueue;
	private final Thread updaterThread;
	private final UpdateHandler updateHandler;
	
	private boolean done = false;
	private boolean animating = false;
	
	public WorldUpdateManager(UpdateHandler updateHandler) {
		this.updateHandler = updateHandler;
		updateQueue = new LinkedBlockingQueue<WorldTimeMessage>();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				threadTask();
			}
		};
		updaterThread = new Thread(runnable, "World Updater");
		updaterThread.setDaemon(true);
	}
	
	public void queueUpdate(WorldTimeMessage message) {
		updateQueue.add(message);
	}
	
	public void start() {
		updaterThread.start();
	}
	
	public void stop() {
		done = true;
		animating = false;
		updaterThread.interrupt();
		try {
			updaterThread.join();
		}
		catch (InterruptedException e) {
			throw new RuntimeException("Should not be interrupted here.");
		}
	}
	
	public void animationStart() {
		animating = true;
	}
	
	public void animationStop() {
		if (animating) {
			animating = false;
			updaterThread.interrupt();
		}
	}
	
	/**
	 * This method is called within the updaterThread.
	 */
	private void threadTask() {
		while (!done) {
			try {
				WorldTimeMessage message = updateQueue.take();
				handleMessage(message);
			} catch (InterruptedException e) {
				// If done is true, will stop after this catch block.
			}
		}
	}
	
	/**
	 * This method is called within the updaterThread.
	 */
	private synchronized void handleMessage(final WorldTimeMessage message) throws InterruptedException {
		// Handle the message, which may set animating to true.
		try {
			SwingUtilities.invokeAndWait(new Runnable() {
				@Override
				public void run() {
					updateHandler.handleWorldTimeMessage(message);
				}
			});
		} catch (InvocationTargetException e1) {
			throw new RuntimeException(e1);
		}
		while (animating) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
		if (updateQueue.isEmpty()) {
			updateHandler.outOfUpdates();
		}
	}
}
